// deepClone深拷贝
// 例：deepClone(Obj||Array)
const getType = obj => {
  var toString = Object.prototype.toString
  var map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
    '[object Symbol]': 'symbol'
  }
  if (obj instanceof Element) {
    // 因为对不同标签，toString会返回对应不同标签的构造函数
    return 'element'
  }
  return map[toString.call(obj)]
}

const getRegExp = re => {
  var flags = ''
  if (re.global) flags += 'g'
  if (re.ignoreCase) flags += 'i'
  if (re.multiline) flags += 'm'
  return flags
}

/**
 * deep clone
 * @param  {[type]} parent object 需要进行克隆的对象
 * @return {[type]}        深克隆后的对象
 */
const deepClone = oldObj => {
  // 维护两个储存循环引用的数组
  const oldObjArr = []
  const newObjArr = []

  const clone = oldObj => {
    let newObj, proto

    const type = getType(oldObj)

    switch (type) {
      case 'boolean':
      case 'number':
      case 'string':
      case 'null':
      case 'undefined':
      case 'function': {
        return oldObj
        break
      }
      case 'symbol': {
        return Symbol(Symbol.keyFor(oldObj).toString())
        break
      }
      case 'array': {
        newObj = []
        break
      }
      case 'regExp': {
        newObj = new RegExp(oldObj.source, getRegExp(oldObj))
        if (oldObj.lastIndex) newObj.lastIndex = oldObj.lastIndex
        break
      }
      case 'date': {
        newObj = new Date(oldObj.getTime())
        break
      }
      // case 'obj':
      default: {
        // 处理对象原型
        proto = Object.getPrototypeOf(oldObj)
        // 利用Object.create切断原型链
        newObj = Object.create(proto)
        break
      }
    }

    // 处理循环引用
    const index = oldObjArr.indexOf(oldObj)
    if (index !== -1) {
      // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
      return newObjArr[index]
    }

    oldObjArr.push(oldObj)
    newObjArr.push(newObj)
    /* 数组和对象都可以用forin语句，虽然数组使用forin会有一个问题(具体看最下面）。
  但是这里不会影响，所以这么用 
  */
    for (let i in oldObj) {
      // 递归
      newObj[i] = clone(oldObj[i])
    }

    return newObj
  }

  return clone(oldObj)
}

export default deepClone
